#![allow(async_fn_in_trait)]
pub use ntex;
pub use ntex::web;
use play_api::*;

use std::net::SocketAddr;
use std::sync::Arc;
pub struct BackEndSever<A, B, C, D, E> {
    pub downloader: A,
    pub database: B,
    pub auth: C,
    pub filesystem: D,
    pub versiongetter: E,
}

impl<A, B, C, D, E> BackEndImpl<ntex::server::Server> for BackEndSever<A, B, C, D, E>
where
    A: Downloader + Send + Sync + 'static,
    B: AuthDB + ConfigDB + Send + Sync + 'static,
    C: OfflineAuth + Send + Sync + 'static,
    D: FileCheck + Send + Sync + 'static,
    E: GetVersion + Send + Sync + 'static,
{
    type Downloader = A;
    type Database = B;
    type Auth = C;
    type FileSystem = D;
    type VersionGetter = E;
    type Error = std::io::Error;
    fn new(
        downloader: Self::Downloader,
        database: Self::Database,
        auth: Self::Auth,
        filesystem: Self::FileSystem,
        versiongetter: Self::VersionGetter,
    ) -> Self {
        Self {
            downloader,
            database,
            auth,
            filesystem,
            versiongetter,
        }
    }
    async fn start(
        self: Arc<Self>,
        ip: [u8; 4],
        port: u16,
    ) -> Result<ntex::server::Server, Self::Error> {
        let this = self.clone();
        let app = move || {
            web::App::new()
                .state(this.clone())
                .route("/minecraft", web::get().to(Self::version_list))
                .route("/config", web::get().to(Self::config_get))
                .route("/config", web::post().to(Self::config_set))
        };
        Ok(web::HttpServer::new(app)
            .bind(SocketAddr::from((ip, port)))?
            .disable_signals()
            .run())
    }
}
pub trait BackEndMethods {
    async fn version_list(
        state: web::types::State<Arc<Self>>,
    ) -> Result<impl web::Responder, web::Error>;
    async fn config_get(
        state: web::types::State<Arc<Self>>,
    ) -> Result<impl web::Responder, web::Error>;
    async fn config_set(
        state: web::types::State<Arc<Self>>,
        data: web::types::Json<LauncherConfig>,
    ) -> Result<impl web::Responder, web::Error>;
}

impl<A, B, C, D, E> BackEndMethods for BackEndSever<A, B, C, D, E>
where
    A: Downloader + Send + Sync + 'static,
    B: AuthDB + ConfigDB + Send + Sync + 'static,
    C: OfflineAuth + Send + Sync + 'static,
    D: FileCheck + Send + Sync + 'static,
    E: GetVersion + Send + Sync + 'static,
{
    async fn version_list(
        state: web::types::State<Arc<Self>>,
    ) -> Result<impl web::Responder, web::Error> {
        let e = &state.versiongetter;
        let data = e
            .get_version_list()
            .await
            .map_err(|e| web::error::ErrorInternalServerError(e.to_string()))?;
        let json = serde_json::to_string(&data)?;
        Ok(json)
    }
    async fn config_get(
        state: web::types::State<Arc<Self>>,
    ) -> Result<impl web::Responder, web::Error> {
        let b = &state.database;
        let data = b
            .get()
            .map_err(|e| web::error::ErrorInternalServerError(e.to_string()))?;
        let json = serde_json::to_string(&data)?;
        Ok(json)
    }
    async fn config_set(
        state: web::types::State<Arc<Self>>,
        data: web::types::Json<LauncherConfig>,
    ) -> Result<impl web::Responder, web::Error> {
        let b = &state.database;
        let data = data.0;
        b.set(&data)
            .map_err(|e| web::error::ErrorInternalServerError(e.to_string()))?;
        Ok(web::HttpResponse::Ok())
    }
}
